#include <curses.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>

#define DOWN 0
#define UP 1
#define LEFT 0
#define RIGHT 1
#define LEVEL_INTERVAL 20	//每级的时间间隔

WINDOW *win = NULL;
int x2, y2, x3, y3, x4, y4, x5, y5;
int baffle_x, baffle_y, baffle_len;
int ball_x, ball_y;
int dir_y = DOWN, dir_x = LEFT;
double radian = 0.78539815;
int over = 0;
int level = 0;
int delay = 400;
char buf[20];

int set_ticker(int n_msecs);
void on_alarm(int signo);

void on_input(int signum)
{
	int key;
	key = wgetch(win);
	if (key == 'q') {
		set_ticker(0);
		endwin();
		exit(0);
	}
	if (key == KEY_LEFT) {
		if (baffle_x > COLS / 4) {
			mvwhline(win, baffle_y, baffle_x, ' ', baffle_len);
			baffle_x--;
		}
	}
	else if (key == KEY_RIGHT) {
		if (baffle_x + baffle_len < COLS / 4 + COLS / 2 + 1) {
			mvwhline(win, baffle_y, baffle_x, ' ', baffle_len);
			baffle_x++;
		}
	}
	else if (key == KEY_UP) {
		 if (over){
			if (delay > 0) {
				level++;
				delay -= level * LEVEL_INTERVAL;
				sprintf(buf, "%d", level + 1);
				mvwaddstr(win, (y2 + y3) / 2, x2 + 2 + 6, buf);
			}
		}
	}
	else if (key == KEY_DOWN) {
		if (over) {
			if (level > 0) {
				level--;
				delay += level * LEVEL_INTERVAL;
				sprintf(buf, "%d", level + 1);
				mvwaddstr(win, (y2 + y3) / 2, x2 + 2 + 6, buf);
			}
		}
	}
	else if (key == 'n') {
		mvwaddstr(win, (y2 + y3) / 2, (x2 + x3) / 2 - 4, "          ");
		refresh();
		over = 0;
		ball_y = (y2 + y3) / 2;
		ball_x = (x2 + x3) / 2;
		signal(SIGALRM, on_alarm);
		set_ticker(delay);
	}
	else return;
	mvwhline(win, baffle_y, baffle_x, '-', baffle_len);
	//refresh();
}
void on_alarm(int signo)
{
	signal(SIGALRM, SIG_IGN);
	mvwaddch(win, ball_y, ball_x, 'o');
	refresh();
	mvwaddch(win, ball_y, ball_x, ' ');
	if (ball_y >= baffle_y - 1 && (ball_x >= baffle_x && ball_x <= baffle_x + baffle_len)) {
		ball_y--;
		dir_y = UP;
	}
	else if (ball_y >= baffle_y - 1 && (ball_x < baffle_x || ball_x > baffle_x + baffle_len)) {
		mvwaddstr(win, (y2 + y3) / 2, (x2 + x3) / 2 - 4, "Game Over!");
		refresh();
		over = 1;
	}
	else if (ball_y <= y2 + 1) {
		ball_y++;
		dir_y = DOWN;
	}
	else {
		if (dir_y == UP)
			ball_y--;
		else if (dir_y == DOWN)
			ball_y++;
	}
	if (ball_x >= x2 - 1) {
		ball_x--;
		dir_x = LEFT;
	}
	else if (ball_x <= x5 + 1) {
		ball_x++;
		dir_x = RIGHT;
	}
	else {
		if (dir_x == LEFT)
			ball_x--;
		else if (dir_x == RIGHT)
			ball_x++;
	}
	if (!over)
		signal(SIGALRM, on_alarm);
}

int main(int argc, char *argv[])
{
	win = initscr();
	clear();
	noecho();
	cbreak();
	keypad(win, TRUE);
	box(win, '|', '-');
	x5 = COLS / 4;
	y5 = LINES / 4;
	x2 = COLS / 4 + COLS / 2;
	y2 = LINES / 4;
	x3 = COLS / 4;
	y3 = LINES / 4 + LINES / 2;
	x4 = COLS / 4 + COLS/ 2;
	y4 = LINES / 4 + LINES / 2;
	mvwhline(win, y5, x5, '-', COLS / 2);
	mvwvline(win, y5, x5, '|', LINES / 2);
	mvwvline(win, y2, x2, '|', LINES / 2);
	mvwaddchstr(win, y5, x5, (const chtype *)"+");
	mvwaddchstr(win, y2, x2, (const chtype *)"+");
	baffle_len = COLS / 4 / 2;
	baffle_x = COLS / 2 - baffle_len / 2;
	baffle_y = LINES / 4 + LINES / 2;
	mvwhline(win, baffle_y, baffle_x, '-', baffle_len);
	ball_y = (y2 + y3) / 2;
	ball_x = (x2 + x3) / 2;
	mvwaddstr(win, (y2 + y3) / 2, x2 + 2, "Level:");
	sprintf(buf, "%d", level + 1);
	mvwaddstr(win, (y2 + y3) / 2, x2 + 2 + 6, buf);
	mvwaddstr(win, (y2 + y3) / 2 + 1, x2 + 2, "q:quit");
	mvwaddstr(win, (y2 + y3) / 2 + 2, x2 + 2, "n:new");
	refresh();

	signal(SIGIO, on_input);
	fcntl(STDIN_FILENO, F_SETOWN, getpid());
	fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_ASYNC);
	signal(SIGALRM, on_alarm);
	set_ticker(400);
	while (1)
		pause();

	set_ticker(0);
	endwin();
	return 0;
}

int set_ticker(int n_msecs)
{
	struct itimerval timer;
	long n_sec, n_usecs;

	n_sec = n_msecs / 1000;
	n_usecs = (n_msecs % 1000) * 1000L;

	timer.it_interval.tv_sec = n_sec;
	timer.it_interval.tv_usec = n_usecs;
	timer.it_value.tv_sec = n_sec;
	timer.it_value.tv_usec = n_usecs;
	return setitimer(ITIMER_REAL, &timer, NULL);
}
